//+--------------------------------------------------------------------
//| Copyright(c) 2012 - an.vuongngocduy					
//| This file is a part of Compact Game Engine (CGE)	
//+--------------------------------------------------------------------
#ifndef __CGE_ARRAYLIST_H__
#define __CGE_ARRAYLIST_H__

#include "cgeAllocator.h"
#include "cgeList.h"

namespace cge
{
	namespace std
	{
		//! Works as an enhanced array , self-allocated array
		template<class TVal, class TAlloc=Allocator<TVal>>
		class ArrayList : public List<TVal>
		{
		public:
			//! Default constructor.
			ArrayList()
				: mSize(0), mCapacity(0), mElements(null)
			{}

			//! Copy constructor.
			ArrayList(const ArrayList& arrLs)
				: mSize(0), mCapacity(0), mElements(null)
			{
				*this = arrLs;
			}

			//! Construct new ArrayList from a specified list.
			ArrayList(const List<TVal>& ls)
				: mSize(0), mCapacity(0), mElements(null)
			{
				*this = ls;
			}

			//! Construct new ArrayList with pre allocated storage.
			ArrayList(const u32& capacity)
				: mSize(0), mCapacity(capacity), mElements(null)
			{
				Reallocate(capacity, true, false);
			}

			//! Construct new ArrayList by copying array of objects
			ArrayList(const TVal* arr, u32 count)
				: mSize(0), mCapacity(0), mElements(null)
			{
				Reallocate(count, true, false);
				for(; mSize < count; mSize++)
					mAlloc.Construct(&mElements[mSize], arr[mSize]);
			}

			//! Destructor.
			~ArrayList()
			{
				clear();
			}

			//! Return number of allocated blocks to hold array of objects.
			u32 Capacity() const
			{
				return mCapacity;
			}
		
			//! Expand the list's storage or shrink it down.
			///	\param capacity : Size to be allocate.
			///	\param forced	: If true(default), always allocate new storage for list.
			///	\param keep		: If true(default), always copy list content to new allocated storage. 
			b8 Reallocate(u32 capacity, b8 forced = true, b8 keep = true)
			{
				if((capacity <= mCapacity && !forced) || !capacity)
					return false;

				TVal* newContainer = mAlloc.Allocate(capacity);
				
				if(newContainer == null) //out of memory
					return false;
				
				u32 size = 0;
				for(u32 i = 0; i < mSize && i < mCapacity && keep; i++)
				{
					if(size < capacity)
					{
						mAlloc.Construct(&newContainer[i], mElements[i]);
						size++;
					}
					mAlloc.Destroy(&mElements[i]);
				}

				mAlloc.Deallocate(mElements);
				mElements = newContainer;
				mSize = size;
				mCapacity = capacity;
				return true;
			}

			//! Assign operator . Copy content of specified list into current list
			ArrayList& operator = (const ArrayList& arrLs)
			{
				return (*this = (List<TVal>&)arrLs);
			}

			ArrayList& operator = (const List<TVal>& ls)
			{
				for(u32 i = 0; i < mSize; i++)
					mAlloc.Destroy(&mElements[i]);
				mSize = 0;			
				Reallocate(ls.Size(), false, false);
				for(; mSize < ls.Size(); mSize++)
					mAlloc.Construct(&mElements[mSize], ls.Get(mSize));
				return *this;
			}

			//! Convert to base List class.
			operator List<TVal>& () const
			{
				return *((List<TVal>*)this);
			}

		private:
			u32 size() const
			{
				return mSize;
			}

			void clear()
			{
				if(mElements != null)
				{
					for(int i = 0; i < mSize; i++)
						mAlloc.Destroy(&mElements[i]);
					mAlloc.Deallocate(mElements);
					mElements = null;
				}
				mSize = mCapacity = 0;
			}

			TVal get(u32 idx) const
			{
				return mElements[idx];
			}

			void set(u32 idx, const TVal& val)
			{
				mAlloc.Destroy(&mElements[idx]);
				mAlloc.Construct(&mElements[idx], val);
			}

			b8 insert(u32 idx, const TVal& val)
			{
				if(idx > mSize)
					return false;

				TVal* arr = mElements;
				
				if(mSize >= mCapacity)
				{
					arr = mAlloc.Allocate(mCapacity + 1);
					
					if(arr == null) //out of memory
						return false;
					mCapacity++;
					for(int i = 0; i <= mSize; i++)
					{
						mAlloc.Construct(&arr[i], (i < idx) ? mElements[i] : (i > idx ? mElements[i + 1] : val));
					}
				}
				else
				{
					for(int i = mSize; i >= idx; i--)
					{
						if(i < mSize)
							mAlloc.Destroy(&arr[i]);
						mAlloc.Construct(&arr[i], (i == idx) ? val : mElements[i - 1]);
					}
				}

				mElements = arr;
				mSize++;	
				return true;
			}

			void remove(u32 idx)
			{
				if(idx >= mSize)
					return;
				for(int i = idx; i < mSize; i++)
				{
					mAlloc.Destroy(&mElements[i]);
					if(i + 1 < mSize)
						mAlloc.Construct(&mElements[i], mElements[i + 1]);
				}
				mSize--;
			}

		private:
			u32 mSize;
			u32 mCapacity;
			TVal* mElements;
			TAlloc mAlloc;
		};
	}
}

#endif//__CGE_ARRAYLIST_H__